﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;

using log4net;
using ConfigUtilLib;
using QueueMgrUtil;
using CommonLib;


namespace DataFetcherProcessorLib
{
    public class DataProcessManager
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static AppConfig _appConfig { get; set; }        
        private static CountdownEvent _countdown = null; // Static due to Thread Use in the same class.
        private const string interfaceStr = "CommonLib.ProcessInterface";

        /// <summary>
        /// 
        /// </summary>
        private  DataProcessManager()
        {
         
        }

        #region PUBLIC_METHODS

        public static void  kickOffProcessingData(AppConfig appCfg)
        {
            _appConfig = appCfg;
            ProcessQueuedRequests();
        }
        #endregion

        #region Private Method 
        /// <summary>
        /// Processing Queue Requests .. 
        /// </summary>
        private static  void ProcessQueuedRequests( )
        {
            log.DebugFormat("Starting Processing Thread");

            BlockReadQueue<QueueWorkItem> WorkItemsQueue;

            WorkItemsQueue = QueueManager.WorkItemsQueue;
            _countdown = null;
            
            while (true)
            {

                QueueWorkItem workItem = WorkItemsQueue.Dequeue();

                if (workItem != null)
                {
                    if (workItem.cmdType == QUEUE_WORK_ITEM_TYPE.PROCESS_END)
                    {
                        log.DebugFormat(" Completed  Processing All Work Items .. ");
                        if( _countdown != null )
                            _countdown.Wait();
                        return;
                    }

                    if (_countdown == null)
                    {
                        _countdown = new CountdownEvent(1);
                    }
                    else
                    {
                        if (_countdown.CurrentCount == 0)
                        {
                            _countdown = new CountdownEvent(1);
                        }
                        else
                        {
                            _countdown.AddCount();
                        }
                    }

                    //  Let Us spin off a thread to execute this. 
                    ThreadPool.QueueUserWorkItem(ProcessQueuedWorkItemThreadFunction, workItem);

                }
            }

        }

        // Thread
        private static  void ProcessQueuedWorkItemThreadFunction( Object args)
        {
            QueueWorkItem workItem = args as QueueWorkItem;            
            //workItem.ProcessRawData(_appConfig);            
        

            FetcherConfigBaseRec fetchRec = workItem.ConfigRecord as FetcherConfigBaseRec;
            if (fetchRec == null)
            {
                log.DebugFormat("Config Record Missing for QueueItem {0}", workItem.uniqueID.ToString());
                return;
            }

            object rawData = workItem.ResultRecord;

            if (rawData == null)
            {
                log.DebugFormat("Raw Carwled Data  Record Missing for QueueItem {0}", workItem.uniqueID.ToString());
                return;
            }

            // Super Cool Process Now .. 

            log.DebugFormat(" Processing Jobs for : {0}", fetchRec.FetcherName);

            List<ProcessorRecord> procRecs =_appConfig.getProcesserListAssociatedWithFetcher(fetchRec.FetcherName);
             // Loop thru Everey Process Object 
            foreach (ProcessorRecord processObject in procRecs )
            {
                Assembly reflectionAssembly = null;
                string asmFullPath = processObject.AssemblyDllName;
                try
                {
                    // 1. Load Library 
                    reflectionAssembly = Assembly.LoadFrom(asmFullPath);
                }
                catch (Exception e)
                {
                    log.DebugFormat("Unable to Load DLL : {0} : {1}", asmFullPath,e.Message);
                    continue;
                }

                if (reflectionAssembly == null) continue;

                foreach (Type classType in reflectionAssembly.GetTypes())
                {

                    // 2. Verify Credentials  

                    if ((classType == null) || (classType.IsClass != true) || (classType.GetInterface(interfaceStr) == null))
                    {
                        continue;
                    }

                    // if all good Invoke The method 
                    ProcessInterface ibaseObject = Activator.CreateInstance(classType) as ProcessInterface;
                    log.DebugFormat("......Dynamically Invoking compute() on {0} Class of {1}", classType.FullName, workItem.uniqueID.ToString());

                    string configPath = processObject.ProcessConfigFile;
                    // Kick it off in a Thread.. 
                    ibaseObject.processrawData(rawData, configPath);
                    log.DebugFormat("Processing :  {0} @ ID  {1} ", fetchRec.FetcherName, workItem.uniqueID.ToString());

                }
            }

            if (_countdown != null)
            {
                _countdown.Signal();
            }

        }


        #endregion

    }
}
